package org.xlx.xlx_oa.warehouse.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xlx.xlx_oa.common.BusinessException;
import org.xlx.xlx_oa.common.Constant;
import org.xlx.xlx_oa.common.enums.ApplyTypeEnums;
import org.xlx.xlx_oa.common.enums.ProcessStatusEnums;
import org.xlx.xlx_oa.common.enums.ProcessTypeEnums;
import org.xlx.xlx_oa.role_user.domain.AdmDepartment;
import org.xlx.xlx_oa.role_user.domain.AdmPosition;
import org.xlx.xlx_oa.role_user.domain.AdmProcess;
import org.xlx.xlx_oa.role_user.domain.AdmUser;
import org.xlx.xlx_oa.role_user.domain.dto.AdmProcessDto;
import org.xlx.xlx_oa.role_user.domain.vo.AuditVo;
import org.xlx.xlx_oa.role_user.mapper.AdmDepartmentMapper;
import org.xlx.xlx_oa.role_user.mapper.AdmPositionMapper;
import org.xlx.xlx_oa.role_user.mapper.AdmUserMapper;
import org.xlx.xlx_oa.role_user.service.AdmProcessService;
import org.xlx.xlx_oa.utils.ThreadLocalUtil;
import org.xlx.xlx_oa.warehouse.convertor.WarehouseConvertor;
import org.xlx.xlx_oa.warehouse.domain.WarehouseApply;
import org.xlx.xlx_oa.warehouse.domain.WarehouseApplyItem;
import org.xlx.xlx_oa.warehouse.domain.WarehouseFacility;
import org.xlx.xlx_oa.warehouse.domain.dto.ApplyItemDto;
import org.xlx.xlx_oa.warehouse.domain.dto.WarehouseApplyDto;
import org.xlx.xlx_oa.warehouse.domain.vo.ApplyItemVo;
import org.xlx.xlx_oa.warehouse.domain.vo.WarehouseApplyVo;
import org.xlx.xlx_oa.warehouse.mapper.WarehouseApplyItemMapper;
import org.xlx.xlx_oa.warehouse.mapper.WarehouseApplyMapper;
import org.xlx.xlx_oa.warehouse.mapper.WarehouseFacilityMapper;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
public class WarehouseApplyService {
    private final WarehouseConvertor warehouseConvertor = WarehouseConvertor.INSTANCE;
    private final ProcessTypeEnums processType = ProcessTypeEnums.Warehouse;
    @Autowired
    private WarehouseApplyMapper warehouseApplyMapper;
    @Autowired
    private AdmProcessService admProcessService;
    @Autowired
    private WarehouseFacilityMapper facilityMapper;
    @Autowired
    private WarehouseApplyItemMapper itemMapper;
    @Autowired
    private AdmUserMapper userMapper;
    @Autowired
    private AdmDepartmentMapper departmentMapper;
    @Autowired
    private AdmPositionMapper positionMapper;

    /**
     * 申请
     *
     * @param applyVo
     * @return
     */
    @Transactional
    public Boolean saveApply(WarehouseApplyVo applyVo) {
        List<ApplyItemVo> itemVos = applyVo.getItemVos();
        if (applyVo.getUserId()==null||applyVo.getUserId()==0){
            Object obj = ThreadLocalUtil.get(Constant.USERID);
            Long userId = (obj == null || "".equals(obj)) ? null : Long.valueOf(obj.toString());
            applyVo.setUserId(userId);
        }
        List<WarehouseApplyItem> applyItems = warehouseConvertor.convertVoToApplyItemList(itemVos);
        WarehouseApply warehouseApply = warehouseConvertor.convertVoToApply(applyVo);
        if (warehouseApply.getId() != null && warehouseApply.getId() != 0) {
            if (!ProcessStatusEnums.Pending.name().equals(warehouseApply.getProcessStatus())) {
                throw new BusinessException("100007", "The application is under review and cannot be modified");
            }

        } else {
            warehouseApply.setProcessStatus(ProcessStatusEnums.Pending.name());
            warehouseApplyMapper.insert(warehouseApply);
            Boolean process = admProcessService.createProcess(warehouseApply.getId(), processType, true);
            if (process) {
                warehouseApply.setProcessStatus(ProcessStatusEnums.Approve.name());
            } else {
                warehouseApply.setProcessStatus(ProcessStatusEnums.Processing.name());
            }
        }
        warehouseApplyMapper.updateById(warehouseApply);
        UpdateWrapper<WarehouseApplyItem> wrapper = new UpdateWrapper<>();
        wrapper.lambda().eq(WarehouseApplyItem::getApplyId, warehouseApply.getId());
        itemMapper.delete(wrapper);
        applyItems.forEach(item -> {
            item.setApplyId(warehouseApply.getId());
            itemMapper.insert(item);
        });
        return true;
    }

    /**
     * 审批
     *
     * @param auditVo
     * @return
     */
    @Transactional
    public Boolean updateAudit(AuditVo auditVo) {
        WarehouseApply warehouseApply = warehouseApplyMapper.selectById(auditVo.getApplyId());
        String status = admProcessService.updateAuditProcess(auditVo, warehouseApply);
        warehouseApply.setProcessStatus(status);
        warehouseApplyMapper.updateById(warehouseApply);
        if (ProcessStatusEnums.Approve.name().equals(status)) {
            QueryWrapper<WarehouseApplyItem> wrapper = new QueryWrapper<>();
            wrapper.lambda().eq(WarehouseApplyItem::getApplyId, warehouseApply.getId());
            List<WarehouseApplyItem> items = itemMapper.selectList(wrapper);
            HashMap<Long, Map<Integer, BigDecimal>> map = new HashMap<>();
            items.forEach(item -> {
                BigDecimal perPrice = null;
                if (item.getTotalAccount() != null && !Objects.equals(item.getTotalAccount(), new BigDecimal(0))) {
                    BigDecimal number = new BigDecimal(item.getNumber());
                    perPrice = item.getTotalAccount().divide(number,6, RoundingMode.HALF_UP);
                }
                HashMap<Integer, BigDecimal> map1 = new HashMap<>();
                map1.put(item.getNumber(), perPrice);
                map.put(item.getFacilityId(), map1);
            });
            QueryWrapper<WarehouseFacility> facilityQueryWrapper = new QueryWrapper<>();
            facilityQueryWrapper.lambda().in(WarehouseFacility::getId, map.keySet());
            List<WarehouseFacility> facilities = facilityMapper.selectList(facilityQueryWrapper);
            if (ApplyTypeEnums.OutStorage.name().equals(warehouseApply.getApplyType())) {
                for (WarehouseFacility facility : facilities) {
                    Set<Integer> integers = map.get(facility.getId()).keySet();
                    ArrayList<Integer> integers1 = new ArrayList<>(integers);
                    facility.setNowCapacity(facility.getNowCapacity() - integers1.get(0));
                }
            }
            if (ApplyTypeEnums.InStorage.name().equals(warehouseApply.getApplyType())) {
                for (WarehouseFacility facility : facilities) {
                    Map<Integer, BigDecimal> map1 = map.get(facility.getId());
                    Set<Integer> integers = map1.keySet();
                    ArrayList<Integer> integers1 = new ArrayList<>(integers);
                    facility.setNowCapacity(facility.getNowCapacity() - integers1.get(0));
                    facility.setPrice(map1.get(integers1.get(0)));
                }
            }
            for (WarehouseFacility facility : facilities) {
                facilityMapper.updateById(facility);
            }
        }
        return true;
    }

    /**
     * 审批详情
     *
     * @param applyId
     * @return
     */
    public List<AdmProcessDto> getDetail(Long applyId) {
        return admProcessService.getListByApplyId(applyId, processType.name());
    }

    /**
     * 待审批列表
     *
     * @param applyVo
     * @return
     */
    public List<WarehouseApplyDto> getBeAuditList(WarehouseApplyVo applyVo) {
        Integer pageNum = applyVo.getPageNum();
        Integer pageSize = applyVo.getPageSize();
        List<AdmProcess> applyList = admProcessService.getApplyList(processType.name());
        HashMap<Long, String> map = new HashMap<>();
        if (applyList.size() == 0) {
            map.put(0L, "");
        } else {
            applyList.forEach(admProcess -> map.put(admProcess.getApplyId(), admProcess.getResult()));
        }
        Page<WarehouseApply> page = new Page<>(pageNum, pageSize);
        QueryWrapper<WarehouseApply> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .in(WarehouseApply::getId, map.keySet());
        if (applyVo.getProcessStatus() != null && (!"".equals(applyVo.getProcessStatus()))) {
            wrapper.lambda().eq(WarehouseApply::getProcessStatus, applyVo.getProcessStatus());
        }
        warehouseApplyMapper.selectPage(page, wrapper);
        List<WarehouseApply> records = page.getRecords();
        List<WarehouseApplyDto> applyDtos = warehouseConvertor.convertToApplyDtoList(records);
        applyDtos.forEach(applyDto -> {
            applyDto.setTackleStatus(map.get(applyDto.getId()));
            this.supplyWarehouseApplyDto(applyDto);
        });
        return applyDtos;
    }

    /**
     * 审批总数
     *
     * @param applyVo
     * @return
     */
    public Integer getBeAuditedCount(WarehouseApplyVo applyVo) {
        List<AdmProcess> applyList = admProcessService.getApplyList(processType.name());
        HashMap<Long, String> map = new HashMap<>();
        if (applyList.size() == 0) {
            map.put(0L, "");
        } else {
            applyList.forEach(admProcess -> map.put(admProcess.getApplyId(), admProcess.getResult()));
        }
        QueryWrapper<WarehouseApply> wrapper = new QueryWrapper<>();
        wrapper.lambda()
                .in(WarehouseApply::getId, map.keySet());
        if (applyVo.getProcessStatus() != null && (!"".equals(applyVo.getProcessStatus()))) {
            wrapper.lambda().eq(WarehouseApply::getProcessStatus, applyVo.getProcessStatus());
        }
        return warehouseApplyMapper.selectCount(wrapper);
    }

    /**
     * 补全计划
     *
     * @param applyDto
     */
    public void supplyWarehouseApplyDto(WarehouseApplyDto applyDto) {
        Long userId = applyDto.getUserId();
        AdmUser user = userMapper.selectById(userId);
        applyDto.setFirstName(user.getFirstName());
        applyDto.setMiddleName(user.getMiddleName());
        applyDto.setLastName(user.getLastName());
        if (user.getPositionId() != 0 && user.getPositionId() != null) {
            AdmPosition position = positionMapper.selectById(user.getPositionId());
            applyDto.setPositionTitle(position.getTitle());
        }
        if (user.getDepartmentId() != 0 && user.getDepartmentId() != null) {
            AdmDepartment admDepartment = departmentMapper.selectById(user.getDepartmentId());
            applyDto.setDepartmentTitle(admDepartment.getTitle());
        }
        ProcessStatusEnums statusEnums = ProcessStatusEnums.valueOf(ProcessStatusEnums.class, applyDto.getProcessStatus());
        applyDto.setProcessStatusName(statusEnums.getText());
        ApplyTypeEnums applyTypeEnums = ApplyTypeEnums.valueOf(ApplyTypeEnums.class, applyDto.getApplyType());
        applyDto.setApplyTypeName(applyTypeEnums.getText());
        if (applyDto.getTackleStatus() != null && (!"".equals(applyDto.getTackleStatus()))) {
            ProcessStatusEnums tackleEnums = ProcessStatusEnums.valueOf(ProcessStatusEnums.class, applyDto.getTackleStatus());
            applyDto.setTackleStatusName(tackleEnums.getText());
        }
        QueryWrapper<WarehouseApplyItem> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(WarehouseApplyItem::getApplyId, applyDto.getId());
        List<WarehouseApplyItem> itemList = itemMapper.selectList(wrapper);
        List<ApplyItemDto> applyItemDtos = warehouseConvertor.convertToApplyItemDtoList(itemList);
        applyItemDtos.forEach(applyItemDto -> {
            WarehouseFacility facility = facilityMapper.selectById(applyItemDto.getFacilityId());
            applyItemDto.setFacilityTitle(facility.getTitle());
        });
        applyDto.setItems(applyItemDtos);
    }

    /**
     * 申请列表
     *
     * @param applyVo
     * @return
     */
    public List<WarehouseApplyDto> getApplyList(WarehouseApplyVo applyVo) {
        Integer pageNum = applyVo.getPageNum();
        Integer pageSize = applyVo.getPageSize();
        String status = applyVo.getProcessStatus();
        Object obj = ThreadLocalUtil.get(Constant.USERID);
        Long userId = (obj == null || "".equals(obj)) ? null : Long.valueOf(obj.toString());
        QueryWrapper<WarehouseApply> queryWrapper = new QueryWrapper<>();
        Page<WarehouseApply> page = new Page<>(pageNum, pageSize);
        queryWrapper.lambda()
                .eq(WarehouseApply::getUserId, userId);
        if (status != null && (!"".equals(status))) {
            queryWrapper.lambda()
                    .eq(WarehouseApply::getProcessStatus, status);
        }
        warehouseApplyMapper.selectPage(page, queryWrapper);
        List<WarehouseApplyDto> applyDtos = warehouseConvertor.convertToApplyDtoList(page.getRecords());
        applyDtos.forEach(applyDto -> this.supplyWarehouseApplyDto(applyDto));
        return applyDtos;
    }

    /**
     * 可选择最大数量
     * @param facilityId
     * @param type
     * @return
     */
    public Integer getCountByFacilityId(Long facilityId, String type) {
        WarehouseFacility facility = facilityMapper.selectById(facilityId);
        int totalNumber = 0;
        if (ApplyTypeEnums.Order.name().equals(type)) {
            QueryWrapper<WarehouseApply> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                    .eq(WarehouseApply::getProcessStatus, ProcessStatusEnums.Processing.name())
                    .eq(WarehouseApply::getApplyType, ApplyTypeEnums.InStorage.name())
                    .or()
                    .eq(WarehouseApply::getApplyType, ApplyTypeEnums.Order.name());
            List<WarehouseApply> applies = warehouseApplyMapper.selectList(queryWrapper);
            ArrayList<Long> ids = new ArrayList<>();
            applies.forEach(apply -> ids.add(apply.getId()));
            QueryWrapper<WarehouseApplyItem> itemQueryWrapper = new QueryWrapper<>();
            itemQueryWrapper.lambda().in(WarehouseApplyItem::getApplyId, ids)
                    .eq(WarehouseApplyItem::getFacilityId, facilityId);
            List<WarehouseApplyItem> items = itemMapper.selectList(itemQueryWrapper);
            for (WarehouseApplyItem item : items) {
                totalNumber += item.getNumber();
            }
            return facility.getMaxCapacity() - facility.getNowCapacity() - totalNumber;
        }
        if (ApplyTypeEnums.OutStorage.name().equals(type)) {
            QueryWrapper<WarehouseApply> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda()
                    .eq(WarehouseApply::getProcessStatus, ProcessStatusEnums.Processing.name())
                    .eq(WarehouseApply::getApplyType, ApplyTypeEnums.OutStorage.name());
            List<WarehouseApply> applies = warehouseApplyMapper.selectList(queryWrapper);
            ArrayList<Long> ids = new ArrayList<>();
            applies.forEach(apply -> ids.add(apply.getId()));
            if (ids.size()==0){
                ids.add(0L);
            }
            QueryWrapper<WarehouseApplyItem> itemQueryWrapper = new QueryWrapper<>();
            itemQueryWrapper.lambda().in(WarehouseApplyItem::getApplyId, ids)
                    .eq(WarehouseApplyItem::getFacilityId, facilityId);
            List<WarehouseApplyItem> items = itemMapper.selectList(itemQueryWrapper);
            for (WarehouseApplyItem item : items) {
                totalNumber += item.getNumber();
            }
            return facility.getNowCapacity() - totalNumber;
        }
        return 0;
    }

    /**
     * 申请总数
     *
     * @param applyVo
     * @return
     */
    public Integer getAppliedCount(WarehouseApplyVo applyVo) {
        String status = applyVo.getProcessStatus();
        Object obj = ThreadLocalUtil.get(Constant.USERID);
        Long userId = (obj == null || "".equals(obj)) ? null : Long.valueOf(obj.toString());
        QueryWrapper<WarehouseApply> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(WarehouseApply::getUserId, userId);
        if (status != null && (!"".equals(status))) {
            queryWrapper.lambda()
                    .eq(WarehouseApply::getProcessStatus, status);
        }
        return warehouseApplyMapper.selectCount(queryWrapper);
    }
}
